﻿using System;
using System.ComponentModel;
using System.Web;
using System.Web.UI;
using System.Web.UI.HtmlControls;
using System.Web.UI.WebControls;
using bATR.Config;

namespace bATR.Controls
{

	/// <summary>
	/// The bATR usercontrol which generates bATR'd imageUrls for you avoiding the clients replacement 
	/// </summary>
	public class bATRImage : UserControl, IButtonControl
	{
		#region private-properties

		private bATRConfig _bATRConfig;

		private HtmlGenericControl textHeader;
		
		// The LinkButton which we use for Click/Command events.
		private LinkButton _btn;
		
		// Some properties for making the good decessions in case of bATR with Click/Command-event
		private bool _hasCommandEvent;
		private bool _hasClickEvent;
		private bool _createAnchor;

		#endregion

		/// <summary>
		/// Constructor, initializes the config
		/// </summary>
		public bATRImage()
		{
			bATRConfigLogic bcl = new bATRConfigLogic();
			this._bATRConfig = bcl.Config;
			
			_btn = new LinkButton();
		}


		/// <summary>
		/// Url, if you want the image to be linked
		/// </summary>
		[Browsable(true)]
		[Category("Behavior")]
		[Description("")]
		public string LinkUrl
		{
			get { return ((string)ViewState["LinkUrl"]); }
			set 
			{ 
				ViewState["LinkUrl"] = value;
				_createAnchor = !string.IsNullOrEmpty(value); // so we can check this later in Command/Click (we can have only one)
			}
		}


		/// <summary>
		/// The text that must be written on image
		/// </summary>
		[Browsable(true)]
		[Category("Behavior")]
		[Description("")]
		public string Text
		{
			get
			{
				if (ViewState["Text"] == null)
					return string.Empty;

				return ((string)ViewState["Text"]);
			}
			set { ViewState["Text"] = value; _btn.ToolTip = value; }
		}


		/// <summary>
		/// Theme to be used, 'default' by default
		/// </summary>
		[Browsable(true)]
		[Category("Behavior")]
		[Description("")]
		public string Theme
		{
			get
			{
				if (ViewState["Theme"] == null || ViewState["Theme"].ToString().Length == 0)
					return "default";

				return ((string)ViewState["Theme"]);
			}
			set { ViewState["Theme"] = value; }
		}


		/// <summary>
		/// Style to format the text with
		/// </summary>
		[Browsable(true)]
		[Category("Behavior")]
		[Description("")]
		public string Style
		{
			get { return ((string)ViewState["Style"]); }
			set { ViewState["Style"] = value; }
		}


		/// <summary>
		/// Style to format the text with on mouse over
		/// </summary>
		[Browsable(true)]
		[Category("Behavior")]
		[Description("")]
		public string StyleOnMouseOver
		{
			get { return ((string)ViewState["StyleOnMouseOver"]); }
			set { ViewState["StyleOnMouseOver"] = value; }
		}


		/// <summary>
		/// 
		/// </summary>
		[Browsable(true)]
		[Category("Behavior")]
		[Description("")]
		public int MaxWidth
		{
			get
			{
				if (ViewState["MaxWidth"] == null)
					return 0;

				return ((int)ViewState["MaxWidth"]);
			}
			set { ViewState["MaxWidth"] = value; }
		}


		/// <summary>
		/// 
		/// </summary>
		[Browsable(true)]
		[Category("Behavior")]
		[Description("")]
		public string FontColor
		{
			get
			{
				if (ViewState["FontColor"] == null)
					return "0";

				return ((string)ViewState["FontColor"]);
			}
			set { ViewState["FontColor"] = value; }
		}




		/// <summary>
		/// 
		/// </summary>
		[Browsable(true)]
		[Category("Behavior")]
		[Description("")]
		public string BackgroundColor
		{
			get
			{
				if (ViewState["BackgroundColor"] == null)
					return "0";

				return ((string)ViewState["BackgroundColor"]);
			}
			set { ViewState["BackgroundColor"] = value; }
		}
		


		/// <summary>
		/// Load some defaults
		/// </summary>
		/// <param name="e"></param>
		protected override void OnLoad(EventArgs e)
		{
			RegisterbATRStyleSheet();			

			if (_bATRConfig.EnablebATRHeaderInfo)
				RegisterbATRInfo();
						
			base.OnLoad(e);
		}
		
		
		/// <summary>
		/// Lets render the control
		/// </summary>
		/// <param name="e"></param>
		protected override void OnPreRender(EventArgs e)
		{
			string bATRPath = _bATRConfig.ImagePath;

			// Relative or absolute url? 
			if (!_bATRConfig.ImagePath.StartsWith("http"))
				bATRPath = VirtualPathUtility.ToAbsolute(_bATRConfig.ImagePath);
			if (!bATRPath.EndsWith("/"))
				bATRPath += "/";


			bATRConfigLogic bcl = new bATRConfigLogic();
			bATRStyle style = bcl.GetStyle(this.Theme, this.Style);
			string headerHtmlTag = style.HeaderHtmlTag;
			HtmlGenericControl textSpan = null;
			if (!string.IsNullOrEmpty(headerHtmlTag))
			{

				// Create the header-tag
				textHeader = new HtmlGenericControl(headerHtmlTag);
				
				// Create a span around the text in the header, so we still can apply style
				// on the header-tag itself
				textSpan = new HtmlGenericControl("span");
				textSpan.InnerText = this.Text;
				
				// Apply style to the span (to hide the text)
				textSpan.Attributes.Add("class", "bATR_Hdr_NP");
				if (_bATRConfig.ReplaceImagesOnPrint && !string.IsNullOrEmpty(headerHtmlTag))
					textSpan.Attributes["class"] = "bATR_Hdr_P";

				if (_hasClickEvent || _hasCommandEvent) // add the header to the LinkButton (Click/Command)
					_btn.Controls.Add(textHeader);
				else
					this.Controls.Add(textHeader);
			}


			// Create a new Html image Tag with the encoded text as url
			HtmlImage bATRImage = new HtmlImage();
			bATRImage.Src = bATRPath + this.Theme + "," + this.Style + "," + EncodeTo64(this.Text) + "," + this.MaxWidth.ToString() + "," + this.FontColor.Replace("#", "") + "," + this.BackgroundColor.Replace("#", "") + _bATRConfig.ImageExtension + ((!_bATRConfig.CachingEnabled) ? "?t=" + DateTime.Now.Ticks.ToString() : string.Empty);
			bATRImage.Alt = this.Text;
			bATRImage.Attributes["title"] = this.Text;

			if (string.IsNullOrEmpty(headerHtmlTag))
				bATRImage.Attributes["class"] = "bATR_Img";
			else if (_bATRConfig.ReplaceImagesOnPrint && !string.IsNullOrEmpty(headerHtmlTag))
				bATRImage.Attributes["class"] = "bATR_Img_NP";
			
			// add mouseover effects
			if (!string.IsNullOrEmpty(this.StyleOnMouseOver))
			{
				string hoverImageUrl = bATRPath + this.Theme + "," + this.StyleOnMouseOver + "," + EncodeTo64(this.Text) + "," + this.MaxWidth.ToString() + "," + this.FontColor.Replace("#", "") + "," + this.BackgroundColor.Replace("#", "") + _bATRConfig.ImageExtension + ((!_bATRConfig.CachingEnabled) ? "?t=" + DateTime.Now.Ticks.ToString() : string.Empty);
				string uid = Guid.NewGuid().ToString("N");

				string strHoverJS = string.Format(@"
				imgSrc{0} = new Image();
				imgSrc{0}.src = '{1}';
				imgSrcHov{0} = new Image();
				imgSrcHov{0}.src = '{2}';
				", uid, bATRImage.Src, hoverImageUrl);

				ScriptManager.RegisterClientScriptBlock(this, this.GetType(), string.Format("{0}_js", this.UniqueID), strHoverJS, true);
				
				// add events to the image to trigger mouseOver
				bATRImage.Attributes.Add("onMouseOver", "this.src=imgSrcHov" + uid + ".src;");
				bATRImage.Attributes.Add("onMouseOut", "this.src=imgSrc" + uid + ".src;");
			}


			// A link specified? Than add an anchor!
			if (!string.IsNullOrEmpty(this.LinkUrl))
			{
				HtmlAnchor link = new HtmlAnchor();
				link.HRef = LinkUrl;

				if (textSpan != null)
					link.Controls.Add(textSpan);

				link.Controls.Add(bATRImage);

				if (textHeader != null)
					textHeader.Controls.Add(link);
				else
					this.Controls.Add(link);
			}
			else
			{
				if (textHeader != null)
				{
					textHeader.Controls.Add(textSpan);
					textHeader.Controls.Add(bATRImage);
				}
				else
				{
					if (_hasClickEvent || _hasCommandEvent) // add the image to the LinkButton (Click/Command)
						_btn.Controls.Add(bATRImage);
					else
						this.Controls.Add(bATRImage);
				}
			}
			base.OnPreRender(e);
		}


		/// <summary>
		/// Encodes the supplied string into a code 64 string
		/// </summary>
		/// <param name="toEncode">a nice piece of text</param>
		/// <returns>that nice piece of text encoded base64</returns>
		static private string EncodeTo64(string toEncode)
		{
			toEncode = HttpUtility.UrlEncode(toEncode);
			byte[] toEncodeAsBytes = System.Text.ASCIIEncoding.ASCII.GetBytes(toEncode);
			string returnValue = System.Convert.ToBase64String(toEncodeAsBytes);

			return returnValue;
		}


		/// <summary>
		/// Writes a tiny stylesheet for header the optional header texts
		/// </summary>
		private void RegisterbATRStyleSheet()
		{
			if (!base.Page.ClientScript.IsClientScriptBlockRegistered(this.GetType(), "bATRStyle"))
			{
				Literal bATRStyle = new Literal();
				bATRStyle.ID = "bATRStyle";
				bATRStyle.Text = @"
<style type=""text/css"" media=""screen"">
	.bATR_Hdr_P { display: none; } 
	.bATR_Hdr_NP { display: none; } 
</style>";
				if (_bATRConfig.ReplaceImagesOnPrint)
					bATRStyle.Text += @"
<style type=""text/css"" media=""print"">
	.bATR_Hdr_P { display: block; } 
	.bATR_Img_NP { display: none; } 
</style>
";
				base.Page.ClientScript.RegisterClientScriptBlock(this.GetType(), "bATRStyle", string.Empty /* Registered in head later */, false);
				this.AddToHeader(bATRStyle);
			}
		}


		/// <summary>
		/// Writes a small bATR info text in the page header
		/// </summary>
		private void RegisterbATRInfo()
		{
			if (!base.Page.ClientScript.IsClientScriptBlockRegistered(this.GetType(), "bATRInfo"))
			{
				Literal bATRInfo = new Literal();
				bATRInfo.ID = "bATRInfo";
				bATRInfo.Text = @"
<!--
This page uses the bATR(barts Anti-aliased Text Replacer) to present smooth
looking text. The bATR is an easy to implement opensource .NET library, more 
information can be found at http://www.codeplex.com/bATR

bATR is developed by Bart de Vries@De Nieuwe Zaak
-->
";
				base.Page.ClientScript.RegisterClientScriptBlock(this.GetType(), "bATRInfo", string.Empty /* Registered in head later */, false);
				
				try
				{
					this.AddToHeader(bATRInfo);
					base.Page.Response.Headers.Add("X-Powered-By", "Better be bATR: Text Image generator, for using custom smooth fonts in websites (C) by Bart de Vries@De Nieuwe Zaak");
				}
				catch (Exception)
				{
					// ignore this one!
				}
			}
		}


		/// <summary>
		/// Adds the supplied control to the page header
		/// </summary>
		/// <param name="controlToAdd"></param>
		/// <exception cref="ApplicationException">when head is not "runat server"</exception>
		private void AddToHeader(Control controlToAdd)
		{
			if (base.Page.Header == null)
				throw new ApplicationException("no page header found");

			base.Page.Header.Controls.Add(controlToAdd);
		}

		#region IButtonControl Members

		/// <summary>
		/// Gets or sets a value indicating whether clicking the bATR causes page validation to occur. 
		/// </summary>
		public bool CausesValidation
		{
			get { return _btn.CausesValidation; }
			set { _btn.CausesValidation = value; }
		}

		/// <summary>
		/// Occurs when the bATR control is clicked.
		/// </summary>
		public event EventHandler Click
		{
			add 
			{
				if (_createAnchor)
					throw new Exception(string.Format("We can't use both a LinkUrl and Click-event for bATR:'{0}'", this.ID));
				
				if(_hasCommandEvent)
					throw new Exception(string.Format("We can't use both a Command-event and Click-event for bATR:'{0}'", this.ID));

				_btn.Click += value;
				
				_btn.ID = this.ID;
				_hasClickEvent = true;

				this.Controls.Add(_btn);
			}
			remove { _btn.Click -= value; }
		}

		/// <summary>
		/// Occurs when the bATR control is clicked.
		/// </summary>
		public event CommandEventHandler Command
		{
			add
			{
				if (_createAnchor)
					throw new Exception(string.Format("We can't use both a LinkUrl and Command-event for bATR:'{0}'", this.ID));

				if (_hasClickEvent)
					throw new Exception(string.Format("We can't use both a Click-event and Command-event for bATR:'{0}'", this.ID));

				_btn.Command += value;
				
				_btn.ID = this.ID;
				_hasCommandEvent = true;

				this.Controls.Add(_btn);
			}
			remove { _btn.Command -= value; }
		}

		/// <summary>
		/// Gets or sets an optional argument that is propagated to the Command event. 
		/// </summary>
		public string CommandArgument
		{
			get { return _btn.CommandArgument; }
			set { _btn.CommandArgument = value; }
		}

		/// <summary>
		/// Gets or sets the command name that is propagated to the Command event.
		/// </summary>
		public string CommandName
		{
			get { return _btn.CommandName; }
			set { _btn.CommandName = value; }
		}

		/// <summary>
		/// Gets or sets the URL of the Web page to post to from the current page when the bATR control is clicked. 
		/// </summary>
		public string PostBackUrl
		{
			get { return _btn.PostBackUrl; }
			set { _btn.PostBackUrl = value; }
		}

		/// <summary>
		/// Gets or sets the name for the group of controls for which the bATR control causes validation when it posts back to the server. 
		/// </summary>
		public string ValidationGroup
		{
			get { return _btn.ValidationGroup; }
			set { _btn.ValidationGroup = value; }
		}

		#endregion
	}

}